﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Threading;
using System.Windows;

using ESRI.ArcGIS.Client;
using ESRI.ArcGIS.Client.Geometry;

namespace eGrid_App
{
    public class VehicleLayer : FeatureLayer, ClonableLayer
    {
        private ScatterLayer scatterlayer;

        public VehicleLayer(ScatterLayer sl) : base()
        {
            scatterlayer = sl;

            ID = "VehiclesLayer";
            Opacity = 1;
            Visible = true;
            Url = "http://asebeast.cpsc.ucalgary.ca:1892/ArcGIS/rest/services/eGridOnline/MapServer/0";
            UpdateCompleted += serverTrucksUpdateCompleted;
            MapLayersManager.Instance.AddLayer(this);
        }

        public void serverTrucksUpdateCompleted(Object sender, EventArgs e)
        {
            InitializeVehicleTimer();
        }

        public Layer CloneLayer()
        {
            GraphicsLayer cloneLayer = new GraphicsLayer();
            cloneLayer.ID = "VehiclesLayer";
            cloneLayer.Opacity = Opacity;
            cloneLayer.Visible = Visible;
            foreach (Graphic g in Graphics)
            {
                Graphic newVehicle = new Graphic();
                newVehicle.Symbol = g.Symbol;
                newVehicle.Geometry = g.Geometry; // graphics of cloned layers share geometry, so that animating one animates them all
                cloneLayer.Graphics.Add(newVehicle);
            }

            return cloneLayer;
            
        }

        #region Vehicle Animation

        // Timer for animating truck movements
        private DispatcherTimer timer;

        //Dictionary to associate vehicle graphics with destination points
        Dictionary<MapPoint, Point> destinations;

        //Random number generator 
        Random rng;

        //Constants
        const int TRUCK_TIMER_SPAN = 1;
        const double TRUCK_MOVE_DISTANCE = 0.0007;

        const double TRUCK_MAX_X = -113.991;
        const double TRUCK_MAX_Y = 51.132;
        const double TRUCK_MIN_X = -114.144;
        const double TRUCK_MIN_Y = 50.922;

        private void InitializeVehicleTimer()
        {
            timer = new DispatcherTimer();
            timer.Interval = new TimeSpan(0, 0, TRUCK_TIMER_SPAN);
            timer.Tick += new EventHandler(Map_Timer_Tick);
            timer.Start();
            rng = new Random();
        }

        private void Map_Timer_Tick(object sender, EventArgs args)
        {

            if ((DispatcherTimer)sender == timer)
            {
                if (destinations == null || destinations.Count == 0)
                {
                    InitializeVehicleDestinations();
                }
                else
                {
                    UpdateVehicleLocations();
                }
            }
        }

        private void UpdateVehicleLocations()
        {
            if (Graphics != null)
            {
                foreach (Graphic g in Graphics)
                {
                    MapPoint mp = (MapPoint)g.Geometry;
                    Point destination;

                    destination = destinations[mp];

                    double XDistance = destination.X - mp.X;
                    double YDistance = destination.Y - mp.Y;
                    double distance = Math.Sqrt(Math.Abs(Math.Pow(YDistance, 2) - Math.Pow(XDistance, 2)));

                    if (distance <= TRUCK_MOVE_DISTANCE) // The vehicle has arrived, select a new destination
                    {
                        destinations.Remove(mp);
                        destination = GenerateRandomVehicleEndpoint();
                        destinations.Add(mp, destination);
                    }
                    else
                    {   //A portion of the movement distance for this tick will be spent travelling in the X dimension, the remaining portion in the Y dimension.
                        //We calculate how far to move in each dimension based on what proportion of the remaining distance lies in that dimension.
                        double XRatio = XDistance / Math.Abs(YDistance);
                        double YRatio = YDistance / Math.Abs(XDistance);
                        double ratioSum = Math.Abs(XRatio) + Math.Abs(YRatio);

                        mp.X += (XRatio / ratioSum) * TRUCK_MOVE_DISTANCE;
                        mp.Y += (YRatio / ratioSum) * TRUCK_MOVE_DISTANCE;

                    }

                }
            }
        }

        private void InitializeVehicleDestinations()
        {
            destinations = new Dictionary<MapPoint, Point>();
            if (Graphics != null)
            {
                foreach (Graphic g in Graphics)
                {
                    MapPoint mp = (MapPoint)g.Geometry;
                    Point destination = GenerateRandomVehicleEndpoint();
                    destinations.Add(mp, destination);
                }
            }
        }

        private Point GenerateRandomVehicleEndpoint()
        {
            //return new Point(-114.07, 51.03); // a point at the middle of town
            return new Point(rng.NextDouble() * (TRUCK_MAX_X - TRUCK_MIN_X) + TRUCK_MIN_X, rng.NextDouble() * (TRUCK_MAX_Y - TRUCK_MIN_Y) + TRUCK_MIN_Y);
        }

        #endregion

    }
}
